﻿using System;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SweetSurvey.Controllers;
using SweetSurvey.Models;

namespace SweetSurvey.Tests.ControllerTests
{
    [TestClass]
    public class AccountControllerTest
    {
        [TestMethod]
        public void LogOnByAjaxTest()
        {
            // Arrange
            AccountController controller = GetAccountController();

            // Act
            var collection = new FormCollection();
            collection.Add("email", MockFormsAuthenticationService.EMAIL);
            collection.Add("password", MockFormsAuthenticationService.PASSWORD);
            ActionResult result = controller.LogOnByAjax(collection);

            // Assert
            Assert.IsInstanceOfType(result, typeof(ContentResult));
            Assert.IsTrue(((ContentResult)result).Content.StartsWith("success"));
        }

        [TestMethod]
        public void LogOffByAjaxTest()
        {
            // Arrange
            AccountController controller = GetAccountController();

            // Act
            ActionResult result = controller.LogOffByAjax();

            // Assert
            Assert.IsInstanceOfType(result, typeof(ContentResult));
            Assert.IsTrue(((ContentResult)result).Content.StartsWith("success"));
            Assert.IsTrue(((MockFormsAuthenticationService)controller.FormsService).SignOut_WasCalled);
        }

        [TestMethod]
        public void RegisterByAjaxTest()
        {
            // Arrange
            AccountController controller = GetAccountController();

            // Act
            var collection = new FormCollection();
            collection.Add("email", MockFormsAuthenticationService.EMAIL);
            collection.Add("password", MockFormsAuthenticationService.PASSWORD);
            ActionResult result = controller.RegisterByAjax(collection);
            collection["email"] = MockFormsAuthenticationService.DUPLICATE_USER_NAME;
            ActionResult resultDuplicate = controller.RegisterByAjax(collection);

            // Assert
            Assert.IsInstanceOfType(result, typeof(ContentResult));
            Assert.IsTrue(((ContentResult)result).Content.StartsWith("success"));
            Assert.IsTrue(((MockFormsAuthenticationService)controller.FormsService).SignIn_WasCalled);
            Assert.AreEqual(((ContentResult)resultDuplicate).Content, MembershipCreateStatus.DuplicateUserName.ToString());
        }

        [TestMethod]
        public void ChangePasswordByAjaxTest()
        {
            // Arrange
            AccountController controller = GetAccountController();

            // Act
            var collection = new FormCollection();
            collection.Add("currentPassword", MockFormsAuthenticationService.OLD_PASSWORD);
            collection.Add("newPassword", MockFormsAuthenticationService.NEW_PASSWORD);
            collection.Add("confirmNewPassword", MockFormsAuthenticationService.NEW_PASSWORD);
            ActionResult result = controller.ChangePasswordByAjax(collection);

            // Assert
            Assert.IsInstanceOfType(result, typeof(ContentResult));
            Assert.IsTrue(((ContentResult)result).Content.StartsWith("success"));
        }

        private static AccountController GetAccountController()
        {
            RequestContext requestContext = new RequestContext(new MockHttpContext(), new RouteData());
            AccountController controller = new AccountController()
            {
                FormsService = new MockFormsAuthenticationService(),
                MembershipService = new MockMembershipService(),
                Url = new UrlHelperAdaptor(requestContext)
            };
            controller.ControllerContext = new ControllerContext()
            {
                Controller = controller,
                RequestContext = requestContext
            };
            return controller;
        }

        private class MockFormsAuthenticationService : IFormsAuthenticationService
        {
            #region Fields

            public bool SignIn_WasCalled;
            public bool SignOut_WasCalled;

            public const string EMAIL = "goodEmail";
            public const string PASSWORD = "goodPassword";
            public const string USER_NAME = EMAIL;
            public const string DUPLICATE_USER_NAME = "duplicateEmail";
            public const string OLD_PASSWORD = "goodOldPassword";
            public const string NEW_PASSWORD = "goodNewPassword";

            #endregion

            public void SignIn(string userName, bool createPersistentCookie)
            {
                // verify that the arguments are what we expected
                Assert.AreEqual(USER_NAME, userName);
                //Assert.IsFalse(createPersistentCookie);

                SignIn_WasCalled = true;
            }

            public void SignOut()
            {
                SignOut_WasCalled = true;
            }
        }

        private class MockHttpContext : HttpContextBase
        {
            private readonly IPrincipal _user = new GenericPrincipal(
                new GenericIdentity(MockFormsAuthenticationService.USER_NAME), null /* roles */);
            private readonly HttpRequestBase _request = new MockHttpRequest();

            public override IPrincipal User
            {
                get
                {
                    return _user;
                }
                set
                {
                    base.User = value;
                }
            }

            public override HttpRequestBase Request
            {
                get
                {
                    return _request;
                }
            }
        }

        private class MockHttpRequest : HttpRequestBase
        {
            private readonly Uri _url = new Uri("http://mysite.example.com/");

            public override Uri Url
            {
                get
                {
                    return _url;
                }
            }
        }

        private class MockMembershipService : IMembershipService
        {
            public int MinPasswordLength
            {
                get { return 10; }
            }

            public bool ValidateUser(string userName, string password)
            {
                return (userName == MockFormsAuthenticationService.USER_NAME
                    && password == MockFormsAuthenticationService.PASSWORD);
            }

            public MembershipCreateStatus CreateUser(string userName, string password, string email)
            {
                if (userName == MockFormsAuthenticationService.DUPLICATE_USER_NAME)
                {
                    return MembershipCreateStatus.DuplicateUserName;
                }

                // verify that values are what we expected
                Assert.AreEqual(MockFormsAuthenticationService.PASSWORD, password);
                Assert.AreEqual(MockFormsAuthenticationService.EMAIL, email);

                return MembershipCreateStatus.Success;
            }

            public bool ChangePassword(string userName, string oldPassword, string newPassword)
            {
                return (userName == MockFormsAuthenticationService.USER_NAME
                    && oldPassword == MockFormsAuthenticationService.OLD_PASSWORD
                    && newPassword == MockFormsAuthenticationService.NEW_PASSWORD);
            }

            #region IMembershipService Members


            public string AdministratorRoleName
            {
                get { return AccountMembershipService.AdministratorRoleName; }
            }

            public string AdministratorUserName
            {
                get { return AccountMembershipService.AdministratorUserName; }
            }

            public string AdministratorPassword
            {
                get { return AccountMembershipService.AdministratorPassword; }
            }

            public void CreateDefaultAdministrator()
            {
                throw new NotImplementedException();
            }

            public bool DefaultAdministratorExists()
            {
                throw new NotImplementedException();
            }

            public void DeleteDefaultAdministrator()
            {
                throw new NotImplementedException();
            }

            #endregion
        }

    }
}
